using System;
using System.Collections.Generic;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;
using System.Data.SqlClient;
using AUSRIS.Framework.Logging;

namespace AUSRIS.Reports.Extensions.Security.Authorization
{
    /// <summary>
    /// DNN based implementation of ISecurityProvider.
    /// Since the roles in DNN are portal-specific, a 
    /// global list of roles required by SSRS is made 
    /// by concatenating the portal ID with the name 
    /// of the role, separated by a dot.
    /// i.e. "1.Student".
    /// </summary>
    public class DNNSecurityProvider : ISecurityProvider
    {
        #region Private Fields

        private string connectionString;
        private string dnnObjectQualifier;
        private static readonly string everyoneRoleName = "Everyone";

        #endregion

        #region Public Properties

        /// <summary>
        /// Connection string to the DNN database
        /// (Set via Dependency Injection)
        /// </summary>
        public string ConnectionString
        {
            get
            {
                return this.connectionString;
            }
            set
            {
                this.connectionString = value;
            }
        }

        /// <summary>
        /// DNN Object Qualifier
        /// (Set via Dependency Injection)
        /// </summary>
        public string DnnObjectQualifier
        {
            get
            {
                return this.dnnObjectQualifier;
            }
            set
            {
                this.dnnObjectQualifier = value;
            }
        }

        #endregion

        #region ISecurityProvider Members

        /// <summary>
        /// Returns true if the principal name is
        /// an existing role or username.
        /// </summary>
        /// <param name="principalName"></param>
        /// <returns></returns>
        public bool IsValidPrincipalName(string principalName)
        {
#if DEBUG
			Log.Debug(Log.GetSource(), "principalName: {0}", principalName);
#endif
            if(string.Compare(principalName, everyoneRoleName, true) == 0)
            {
                return true;
            }
            else
            {
                bool isValid = this.RoleExists(principalName);
                if(!isValid)
                {
                    isValid = this.UsernameExists(principalName);
                }
                return isValid;
            }
        }

        /// <summary>
        /// Returns true if the credentials suppied are valid according the the specified authority
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <param name="authority"></param>
        /// <returns></returns>
        /// <remarks>
        /// If the authority is blank, assumes the default authority, which is the DNN Username/password store
        /// </remarks>
        public bool IsValidCredentials(string userName, string password, string authority)
        {
#if DEBUG
			Log.Debug(Log.GetSource(), "userName: {0}", userName);
#endif
            if(string.Compare(authority, "ClientCertificate", true) == 0)
            {
                return this.UsernameExists(userName);
            }
            else
            {
                return this.IsValidCredentials(userName, password);
            }
        }

        /// <summary>
        /// List the roles currently assigned to the user.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public string[] ListRoles(string userName)
        {
#if DEBUG
			Log.Debug(Log.GetSource(), "userName: {0}", userName);
#endif
            List<string> roles = new List<string>();
            StringBuilder sql = new StringBuilder();
            sql.AppendFormat("SELECT ");
            sql.AppendFormat("	CAST(dbo.{0}Roles.PortalId AS VARCHAR(20)) + '.' + dbo.{0}Roles.RoleName AS RoleName ", this.DnnObjectQualifier);
            sql.AppendFormat("FROM  ");
            sql.AppendFormat("	dbo.{0}UserRoles ", this.DnnObjectQualifier);
            sql.AppendFormat("	INNER JOIN dbo.{0}Users ON dbo.{0}UserRoles.UserId = dbo.{0}Users.UserId ", this.DnnObjectQualifier);
            sql.AppendFormat("	INNER JOIN dbo.{0}Roles ON dbo.{0}UserRoles.RoleId = dbo.{0}Roles.RoleId ", this.DnnObjectQualifier);
            sql.AppendFormat("WHERE ");
            sql.AppendFormat("	dbo.{0}Users.Username = @Username ", this.DnnObjectQualifier);
            sql.AppendFormat("	AND (dbo.{0}UserRoles.EffectiveDate <= GETDATE() OR dbo.{0}UserRoles.EffectiveDate IS NULL) ", this.DnnObjectQualifier);
            sql.AppendFormat("	AND (dbo.{0}UserRoles.ExpiryDate >= GETDATE() OR dbo.{0}UserRoles.ExpiryDate IS NULL);", this.DnnObjectQualifier);
            string commandText = sql.ToString();
            using(SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand command = new SqlCommand(commandText, connection);
                command.Parameters.AddWithValue("@Username", userName);
                connection.Open();
                using(SqlDataReader reader = command.ExecuteReader())
                {
                    while(reader.Read())
                    {
                        roles.Add(reader.GetString(reader.GetOrdinal("RoleName")));
                    }
                    reader.Close();
                }
                connection.Close();
            }
            roles.Add(everyoneRoleName);
            return roles.ToArray();
        }

        /// <summary>
        /// Lists the usernames of the host accounts
        /// </summary>
        public string[] ListAdministrators()
        {
            List<string> userNames = new List<string>();
            StringBuilder sql = new StringBuilder();
            sql.AppendFormat("SELECT ");
            sql.AppendFormat("	dbo.{0}Users.UserName ", this.DnnObjectQualifier);
            sql.AppendFormat("FROM  ");
            sql.AppendFormat("	dbo.{0}Users ", this.DnnObjectQualifier);
            sql.AppendFormat("WHERE ");
            sql.AppendFormat("	dbo.{0}Users.IsSuperUser = 1;", this.DnnObjectQualifier);
            string commandText = sql.ToString();
            using(SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand command = new SqlCommand(commandText, connection);
                connection.Open();
                using(SqlDataReader reader = command.ExecuteReader())
                {
                    while(reader.Read())
                    {
                        userNames.Add(reader.GetString(reader.GetOrdinal("UserName")));
                    }
                    reader.Close();
                }
                connection.Close();
            }
            return userNames.ToArray();
        }

        #endregion

        #region Private methods

        private bool RoleExists(string roleName)
        {
#if DEBUG
			Log.Debug(Log.GetSource(), "roleName: {0}", roleName);
#endif
            bool exists = false;
            StringBuilder sql = new StringBuilder();
            sql.AppendFormat("SELECT COUNT(*) FROM ");
            sql.AppendFormat("( ");
            sql.AppendFormat("	SELECT ");
            sql.AppendFormat("	CAST(dbo.{0}Roles.PortalId AS VARCHAR(20)) + '.' + dbo.{0}Roles.RoleName AS RoleName ", this.DnnObjectQualifier);
            sql.AppendFormat("	FROM ");
            sql.AppendFormat("	dbo.{0}Roles ", this.DnnObjectQualifier);
            sql.AppendFormat(") Roles WHERE RoleName = @RoleName;");
            string commandText = sql.ToString();
            using(SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand command = new SqlCommand(commandText, connection);
                command.Parameters.AddWithValue("@RoleName", roleName);
                connection.Open();
                object returnValue = command.ExecuteScalar();
                connection.Close();
                if(returnValue != null && returnValue != DBNull.Value)
                {
                    exists = int.Parse(returnValue.ToString()) != 0;
                }
            }
            return exists;
        }

        private bool UsernameExists(string userName)
        {
#if DEBUG
			Log.Debug(Log.GetSource(), "userName: {0}", userName);
#endif
            bool exists = false;
            string commandText = string.Format("SELECT COUNT(*) FROM {0}Users WHERE Username = @Username;", this.DnnObjectQualifier);
            using(SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand command = new SqlCommand(commandText, connection);
                command.Parameters.AddWithValue("@Username", userName);
                connection.Open();
                object returnValue = command.ExecuteScalar();
                connection.Close();
                if(returnValue != null && returnValue != DBNull.Value)
                {
                    exists = int.Parse(returnValue.ToString()) != 0;
                }
            }
            return exists;
        }

        private bool IsValidCredentials(string userName, string password)
        {
#if DEBUG
			Log.Debug(Log.GetSource(), "userName: {0}, password: {1}", userName, password);
#endif
            bool isValid = false;
            try
            {
                isValid = Membership.ValidateUser(userName, password);
            }
            catch(Exception ex)
            {
                Log.Fatal(Log.GetSource(), ex.ToString());
            }
#if DEBUG
			Log.Debug(Log.GetSource(), "isValid: {0}", isValid);
#endif
            return isValid;
        }

        #endregion
    }
}